home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / human interface toolbox / controlbackground / controlbackground.c next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  10.6 KB  |  401 lines

  1. /*
  2.     File:        ControlBackground.c
  3.  
  4.     Contains:    ControlBackground, a quickie sample which shows how to
  5.                 affect the background color of a control when drawing it.
  6.  
  7.     Written by: Pete Gontier    
  8.  
  9.     Copyright:    Copyright © 1997-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                 7/19/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24.  
  25. #include <Fonts.h>
  26. #include <Dialogs.h>
  27. #include <QDOffscreen.h>
  28. #include <Gestalt.h>
  29. #include <Appearance.h>
  30. #include <Sound.h>
  31. #include <LowMem.h>
  32.  
  33. #include "MoveableModalDialog.h"
  34.  
  35. enum
  36. {
  37.     kDialogItemIndex_DrawButton = 3,
  38.     kDialogItemIndex_CheckBox
  39. };
  40.  
  41. static GrafPtr LMGetWMgrCPort (void) { return *(GrafPtr*)0x0D2C; }
  42. #define IsColorGrafPort(port) (((port)->portBits.rowBytes & 0xC000) == 0xC000)
  43.  
  44. static pascal OSErr InitMac (void)
  45. {
  46.     MaxApplZone ( );
  47.     InitGraf (&(qd.thePort));
  48.     InitFonts ( );
  49.     InitWindows ( );
  50.     InitMenus ( );
  51.     TEInit ( );
  52.     InitDialogs (nil);
  53.  
  54.     return noErr;
  55. }
  56.  
  57. static pascal OSStatus IsAppearancePresent (Boolean *haveAppearance)
  58. {
  59.     OSStatus err = noErr;
  60.  
  61.     long response;
  62.  
  63.     if (!(err = Gestalt (gestaltAppearanceAttr,&response)))
  64.         *haveAppearance = response & (1 << gestaltAppearanceExists);
  65.     else if (err == gestaltUndefSelectorErr)
  66.     {
  67.         *haveAppearance = false;
  68.         err = noErr;
  69.     }
  70.  
  71.     return err;
  72. }
  73.  
  74. static pascal ControlRef NewUserPaneControl
  75.     (WindowPtr window, const Rect *bounds, Boolean visible, UInt16 featureFlags)
  76. {
  77.     return NewControl (window,bounds,"\p",visible,featureFlags,0,0,kControlUserPaneProc,0);
  78. }
  79.  
  80. static pascal OSErr AtLeastOneParentHasSpecialBackground (ControlRef control, Boolean *alophsb)
  81. {
  82.     //
  83.     //    This function walks up the control hierarchy looking for
  84.     //    a control which provides a special background. We need
  85.     //    to know this in order to decide whether the only way for
  86.     //    us to affect the background color is to go to the trouble
  87.     //    of creating a user pane control.
  88.     //
  89.  
  90.     OSErr err = noErr;
  91.  
  92.     ControlHandle root;
  93.  
  94.     *alophsb = false;
  95.  
  96.     if (!(err = GetRootControl ((**control).contrlOwner, &root)))
  97.     {
  98.         if (!root)
  99.             err = errNoRootControl;
  100.         else
  101.         {
  102.             ControlRef scan = control;
  103.  
  104.             while (scan != root)
  105.             {
  106.                 UInt32 features;
  107.  
  108.                 err = GetSuperControl (scan,&scan);
  109.                 if (err) break;
  110.                 err = GetControlFeatures (scan,&features);
  111.                 if (err) break;
  112.  
  113.                 if (features & kControlHasSpecialBackground)
  114.                 {
  115.                     //
  116.                     //    In at least 8.6 and earlier (and perhaps later),
  117.                     //    CreateRootControl spuriously sets
  118.                     //    kControlHasSpecialBackground. [Radar 2324373]
  119.                     //    We work around this weirdness here by checking
  120.                     //    to see if a proc has been assigned. If the bug
  121.                     //    gets fixed, this code will be a little less
  122.                     //    than optimally efficient but will still be
  123.                     //    correct (I hope).
  124.                     //
  125.  
  126.                     if (scan != root)
  127.                     {
  128.                         *alophsb = true;
  129.                         break;
  130.                     }
  131.                     else
  132.                     {
  133.                         ControlUserPaneBackgroundUPP    upp;
  134.                         Size                            actualSize;
  135.  
  136.                         if (!(err = GetControlData (scan,kControlNoPart,
  137.                             kControlUserPaneBackgroundProcTag,sizeof(upp),(Ptr)&upp,&actualSize)))
  138.                         {
  139.                             if (sizeof (upp) != actualSize)
  140.                                 err = paramErr;
  141.                             else if (upp)
  142.                             {
  143.                                 *alophsb = true;
  144.                                 break;
  145.                             }
  146.                         }
  147.                     }
  148.                 }
  149.             }
  150.         }
  151.     }
  152.  
  153.     return noErr;
  154. }
  155.  
  156. static pascal void ControlUserPaneBackgroundProc (ControlHandle control, ControlBackgroundPtr)
  157. {
  158.     //
  159.     //    This function is called by the user pane CDEF. Its
  160.     //    job is to setup the background of the current graphics
  161.     //    port in some "special" way. All we want is to set the color.
  162.     //    The appearance-savvy thing to do when setting the color is
  163.     //    to also set the pattern (and vice versa).
  164.     //
  165.  
  166.     BackPat (&(qd.white));
  167.     RGBBackColor ((const RGBColor *) GetControlReference (control));
  168. }
  169.  
  170. #pragma mark -
  171.  
  172. static pascal OSErr Draw1ControlWithBackgroundColorViaWindowColorTable
  173.     (ControlRef control, const RGBColor *rgb)
  174. {
  175.     //
  176.     //    Walk the color table of the given control's
  177.     //    window looking for the content color.
  178.     //
  179.  
  180.     AuxWinHandle    auxWinHandle;
  181.     WindowRef        contrlOwner                    = (**control).contrlOwner;
  182.     Boolean            oldColorTableWasDefault        = GetAuxWin (contrlOwner,&auxWinHandle);
  183.     WCTabHandle        winCTabHandle                = (WCTabHandle) ((**auxWinHandle).awCTable);
  184.     short            ctIndex                        = (**winCTabHandle).ctSize;
  185.  
  186.     while (ctIndex > -1)
  187.     {
  188.         ColorSpecPtr rgbScan = ctIndex + (**winCTabHandle).ctTable;
  189.  
  190.         if (rgbScan->value == wContentColor)
  191.         {
  192.             RGBColor savedRGB = rgbScan->rgb;
  193.             rgbScan->rgb = *rgb;
  194.             CTabChanged ((CTabHandle) winCTabHandle);
  195.             Draw1Control (control);
  196.             // assume memory has moved and rgbScan has become stale
  197.             (**winCTabHandle).ctTable [ctIndex].rgb = savedRGB;
  198.             CTabChanged ((CTabHandle) winCTabHandle);
  199.             return noErr; // bail out of function without exiting loop
  200.         }
  201.  
  202.         --ctIndex;
  203.     }
  204.  
  205.     //
  206.     //    If the content color was found, the rest of this function
  207.     //    will not execute; there is a return statement inside the
  208.     //    loop, above.
  209.     //
  210.     //    We take a lack of a content color to mean that we've been
  211.     //    passed a control which lives in a bogus window, and we claim
  212.     //    this is a parameter error. However, it's conceivable that
  213.     //    we could deal with this by either creating a color table
  214.     //    for this window or adding an entry to the table which is
  215.     //    already there. This kind of thing is beyond the scope of
  216.     //    this sample. You only have to worry about this issue if
  217.     //    you are creating your own window color tables and don't
  218.     //    always include a content color. Resource editors generally
  219.     //    create an entry for the content color even if you
  220.     //    only customized some other color in the table.
  221.     //
  222.  
  223.     return paramErr;
  224. }
  225.  
  226. static pascal OSErr Draw1ControlWithBackgroundColorViaOwningGrafPort
  227.     (ControlRef control, const RGBColor *rgb)
  228. {
  229.     //
  230.     //    Our job here is simple; set the background color of the
  231.     //    graphics port into which the control will be drawn and
  232.     //    then draw the control. The interesting wrinkle is that
  233.     //    if the control's owning port is monochrome, it will draw
  234.     //    into the Window Manager's color port. Nasty! The only
  235.     //    way to do the right thing here is to muck with low memory.
  236.     //    There is not even an LM accessor for what we need to do!
  237.     //    This is actively Carbon-hostile, which means this sample
  238.     //    will have to be updated again for Carbon. Sigh...
  239.     //
  240.  
  241.     RGBColor    preservedBackColor;
  242.     GrafPtr        preservedPort            = qd.thePort;
  243.     GrafPtr        targetPort                = (**control).contrlOwner;
  244.  
  245.     if (!IsColorGrafPort (targetPort))
  246.         targetPort = LMGetWMgrCPort ( );
  247.  
  248.     preservedBackColor = ((CGrafPtr) targetPort)->rgbBkColor;
  249.  
  250.     SetPort (targetPort);
  251.     RGBBackColor (rgb);
  252.     Draw1Control (control);
  253.     RGBBackColor (&preservedBackColor);
  254.  
  255.     return noErr;
  256. }
  257.  
  258. static pascal OSErr Draw1ControlWithBackgroundColorViaUserPane
  259.     (ControlRef child, const RGBColor *rgb)
  260. {
  261.     //
  262.     //    This is the most complicated part of this sample.
  263.     //    We call the control which we want to draw "the child".
  264.     //    We create a user pane control which will be the parent of
  265.     //    the child. We associate a special background setup proc
  266.     //    with the user pane. The user pane is considered to be
  267.     //    "behind" the child for purposes of the special background
  268.     //    proc, so the user pane gets to dictate the background for
  269.     //    the child. After we're done drawing the child, we restore
  270.     //    it to its original parent and blow away the user pane.
  271.     //
  272.  
  273.     OSErr err = noErr;
  274.  
  275.     ControlRef parent;
  276.  
  277.     if (!(err = GetSuperControl (child,&parent)))
  278.     {
  279.                 Rect        userPaneBounds    = (**child).contrlRect;
  280.                 GrafPtr        userPaneOwner    = (**child).contrlOwner;
  281.         const    UInt32        userPaneFF        = kControlSupportsEmbedding | kControlHasSpecialBackground;
  282.                 ControlRef    userPane        = NewUserPaneControl (userPaneOwner,&userPaneBounds,true,userPaneFF);
  283.  
  284.         if (!userPane)
  285.             err = nilHandleErr;
  286.         else
  287.         {
  288.             OSErr err2;
  289.  
  290.             if (!(err = EmbedControl (userPane,parent)))
  291.             if (!(err = EmbedControl (child,userPane)))
  292.             {
  293.                 ControlUserPaneBackgroundUPP upp =
  294.                     NewControlUserPaneBackgroundProc (ControlUserPaneBackgroundProc);
  295.  
  296.                 if (!upp)
  297.                     err = nilHandleErr;
  298.                 else
  299.                 {
  300.                     if (!(err = SetControlData (userPane,kControlNoPart,
  301.                         kControlUserPaneBackgroundProcTag,sizeof(upp),(Ptr)&upp)))
  302.                     {
  303.                         SetControlReference (userPane,(long)rgb);
  304.                         Draw1Control (child);
  305.                     }
  306.                     DisposeRoutineDescriptor (upp);
  307.                 }
  308.                 err2 = EmbedControl (child,parent);
  309.                 if (!err) err = err2;
  310.             }
  311.  
  312.             err2 = SetControlVisibility (userPane,false,false);
  313.             if (!err) err = err2;
  314.             DisposeControl (userPane);
  315.         }
  316.     }
  317.  
  318.     return noErr;
  319. }
  320.  
  321. static pascal OSErr Draw1ControlWithBackgroundColor (ControlRef control, const RGBColor *rgb)
  322. {
  323.     //
  324.     //    This function decides which technique to use for drawing
  325.     //    the control with the appropriate background based on what
  326.     //    APIs are available and the state of the control's owning
  327.     //    window.
  328.     //
  329.  
  330.     OSErr err = noErr;
  331.  
  332.     Boolean haveAppearance;
  333.  
  334.     if (!(err = IsAppearancePresent (&haveAppearance)))
  335.     {
  336.         if (!haveAppearance)
  337.             err = Draw1ControlWithBackgroundColorViaWindowColorTable (control,rgb);
  338.         else
  339.         {
  340.             ControlHandle rootControl;
  341.  
  342.             err = GetRootControl ((**control).contrlOwner, &rootControl);
  343.  
  344.             if (err == errNoRootControl || !rootControl)
  345.                 err = Draw1ControlWithBackgroundColorViaOwningGrafPort (control,rgb);
  346.             else
  347.             {
  348.                 Boolean special;
  349.  
  350.                 if (!(err = AtLeastOneParentHasSpecialBackground (control,&special)))
  351.                 {
  352.                     if (!special)
  353.                         err = Draw1ControlWithBackgroundColorViaOwningGrafPort (control,rgb);
  354.                     else
  355.                         err = Draw1ControlWithBackgroundColorViaUserPane (control,rgb);
  356.                 }
  357.             }
  358.         }
  359.     }
  360.  
  361.     return err;
  362. }
  363.  
  364. #pragma mark -
  365.  
  366. void main (void)
  367. {
  368.     if (InitMac ( ))
  369.         SysBeep (10);
  370.     else
  371.     {
  372.         DialogRef dlgRef = GetNewDialog (129,nil,(WindowRef)-1);
  373.         if (dlgRef)
  374.         {
  375.             short itemHit;
  376.  
  377.             SetDialogDefaultItem (dlgRef,kStdOkItemIndex);
  378.             ShowWindow (dlgRef);
  379.             InitCursor ( );
  380.  
  381.             do
  382.             {
  383.                 MoveableModalDialog (NewModalFilterProc(StdFilterProc),&itemHit);
  384.  
  385.                 if (itemHit == kDialogItemIndex_DrawButton)
  386.                 {
  387.                     short iType; Handle iHandle; Rect iRect;
  388.                     RGBColor periwinkle = { 0x5555,0x5555,0xFFFF };
  389.  
  390.                     GetDialogItem (dlgRef,kDialogItemIndex_CheckBox,&iType,&iHandle,&iRect);
  391.                     if (Draw1ControlWithBackgroundColor ((ControlRef)iHandle, &periwinkle))
  392.                         SysBeep (10);
  393.                 }
  394.             }
  395.             while (itemHit != kStdOkItemIndex);
  396.  
  397.             DisposeDialog (dlgRef);
  398.         }
  399.     }
  400. }
  401.